﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BlogManager.ObjectModel;
using BlogManager.Services;
using BlogManager.Properties;
using System.IO;

namespace BlogManager
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            optModificati.Tag = FilterMode.Modificati;
            optNonModificati.Tag = FilterMode.NonModificati;
            optTutti.Tag = FilterMode.Tutti;
            this.Text = Application.ProductName;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            fillCategories(false);
        }

        #region DataBind CheckBoxList categorie

        List<Category> categories = new List<Category>();
        private void fillCategories()
        {
            fillCategories(true);
        }
        private void fillCategories(bool showConnectionError)
        {
            try
            {
                IList<Category> retrievedCategories =
                ServiceRepository.Factory.GetCategoryService().GetAll();

                this.categories = new List<Category>();
                categories.AddRange(retrievedCategories);

                retrievedCategories.Insert(0, new Category());

                bsCategoryFilter.DataSource = retrievedCategories;

                refreshCheckBoxList();
            }
            catch (ConnectionException)
            {
                if (showConnectionError)
                    MsgBox.Error(Resources.ConnectionError);
            }
        }

        private void refreshCheckBoxList()
        {
            chkCategories.DataSource = categories;
            chkCategories.ValueMember = "Id";
            chkCategories.DisplayMember = "Description";
        }
        #endregion

        #region Gestione filtering
        private FilterMode filterMode = FilterMode.Tutti;
        private void optTutti_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton option = sender as RadioButton;
            if (option != null && option.Checked)
            {
                this.filterMode = (FilterMode)option.Tag;
                refreshListView();
            }
        }

        Category filterCategory = null;
        private void cmbFilter_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbFilter.SelectedIndex == 0)
            {
                filterCategory = null;
            }
            else
            {
                this.filterCategory =
                    this.cmbFilter.SelectedItem as Category;
            }

            refreshListView();
        }

        private string filterTag = null;
        private void cmbTags_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (refreshingTags)
                return;

            if (cmbTags.SelectedIndex == 0)
            {
                filterTag = null;
            }
            else
            {
                this.filterTag =
                    this.cmbTags.SelectedItem as string;
            }

            refreshListView();
        }
        #endregion

        #region cmbNumberOfPosts
        private void cmbNumberOfPosts_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsDigit(e.KeyChar) && e.KeyChar != '\b';
        }
        #endregion

        #region Fetch dei post
        private void fillPosts(int numberOfPosts)
        {
            this.modifiedPosts.Clear();
            try
            {
                this.posts = ServiceRepository.Factory.GetPostService().GetPosts(numberOfPosts);
            }
            catch (ConnectionException)
            {
                MsgBox.Error(Resources.ConnectionError);
            }

            refreshTagsList(this.posts);
            refreshListView();
        }
        #endregion

        #region Binding ListView
        IList<Post> posts = new List<Post>();
        private void refreshListView()
        {
            lstPosts.Items.Clear();
            lstPosts.View = View.Details;

            foreach (var post in posts)
            {
                if (filterMode == FilterMode.Tutti ||
                    (filterMode == FilterMode.Modificati && modifiedPosts.Contains(post)) ||
                    (filterMode == FilterMode.NonModificati && !modifiedPosts.Contains(post)))
                {
                    if ((filterCategory == null ||
                         post.ContainsCategory(filterCategory.Id)) &&
                        (filterTag == null ||
                         post.Tags.Contains(filterTag)))

                    {
                        ListViewItem item = new ListViewItem();
                        item.Text = post.Title;
                        item.SubItems.Add(new ListViewItem.ListViewSubItem(item, post.DateCreated.ToShortDateString()));
                        item.SubItems.Add(new ListViewItem.ListViewSubItem(item, post.DateCreated.ToShortTimeString()));

                        item.ToolTipText = (post.PlainText.Length > 500 ?
                            post.PlainText.Substring(0, 497) + "..." :
                            post.PlainText);

                        if (modifiedPosts.Contains(post))
                            setModifiedStyle(item);

                        item.Tag = post;
                        lstPosts.Items.Add(item);
                    }
                }
            }
        }

        private void setModifiedStyle(ListViewItem item)
        {
            item.Font = setItalic(item.Font);
        }

        private void setUnmodifiedStyle(ListViewItem item)
        {
            item.Font = removeItalic(item.Font);
        }

        private Font setItalic(Font font)
        {
            return new Font(font, font.Style | FontStyle.Italic);
        }

        private Font removeItalic(Font font)
        {
            return new Font(font, font.Style & (~FontStyle.Italic));
        }
        #endregion

        #region Navigazione ListView
        private bool changingSelection = false;
        private void lstPosts_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                changingSelection = true;
                updateCheckBoxesLists();
            }
            finally
            {
                changingSelection = false;
            }
        }

        private void updateCheckBoxesLists()
        {
            setAllItemsToUnchecked();
            bool first = true;

            foreach (ListViewItem selectedItem in lstPosts.SelectedItems)
            {
                Post post = selectedItem.Tag as Post;
                if (post != null)
                {
                    for (int i = 0; i < categories.Count; i++)
                    {
                        if (first)
                        {
                            chkCategories.SetItemChecked(i, post.ContainsCategory(categories[i].Id));
                        }
                        else
                        {
                            CheckState state = chkCategories.GetItemCheckState(i);
                            if ((post.ContainsCategory(categories[i].Id) && state != CheckState.Checked) ||
                                (!post.ContainsCategory(categories[i].Id) && state != CheckState.Unchecked))
                            {
                                chkCategories.SetItemCheckState(i, CheckState.Indeterminate);
                            }
                        }
                    }

                    for (int i = 0; i < tags.Count; i++)
                    {
                        if (first)
                        {
                            chkTags.SetItemChecked(i, post.Tags.Contains(tags[i]));
                        }
                        else
                        {
                            CheckState state = chkTags.GetItemCheckState(i);
                            if ((post.Tags.Contains(tags[i]) && state != CheckState.Checked) ||
                                (!post.Tags.Contains(tags[i]) && state != CheckState.Unchecked))
                            {
                                chkTags.SetItemCheckState(i, CheckState.Indeterminate);
                            }
                        }
                    }

                    first = false;
                }
            }
        }

        private void setAllItemsToUnchecked()
        {
            for (int i = 0; i < chkCategories.Items.Count; i++)
            {
                chkCategories.SetItemCheckState(i, CheckState.Unchecked);
            }
            for (int i = 0; i < chkTags.Items.Count; i++)
            {
                chkTags.SetItemCheckState(i, CheckState.Unchecked);
            }
        }
        #endregion

        #region Gestione editing
        private List<Post> modifiedPosts = new List<Post>();
        private void chkCategories_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (changingSelection)
                return;

            Category category = categories[e.Index];
            foreach (ListViewItem selectedItem in lstPosts.SelectedItems)
            {
                Post selectedPost = selectedItem.Tag as Post;
                Category postCategory = selectedPost.GetCategoryById(category.Id);

                if (e.NewValue == CheckState.Checked)
                {
                    if (postCategory == null)
                    {
                        selectedPost.Categories.Add(category);
                        if (!modifiedPosts.Contains(selectedPost))
                            modifiedPosts.Add(selectedPost);
                    }
                }
                else if (e.NewValue == CheckState.Unchecked)
                {
                    if (postCategory != null)
                        selectedPost.Categories.Remove(postCategory);
                    if (!modifiedPosts.Contains(selectedPost))
                        modifiedPosts.Add(selectedPost);
                }

                setModifiedStyle(selectedItem);
            }
            if (filterMode == FilterMode.NonModificati)
                refreshListView();
        }

        private void chkTags_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (changingSelection)
                return;

            string tag = tags[e.Index];
            foreach (ListViewItem selectedItem in lstPosts.SelectedItems)
            {
                Post selectedPost = selectedItem.Tag as Post;
                
                if (e.NewValue == CheckState.Checked)
                {
                    if (!selectedPost.Tags.Contains(tag))
                    {
                        selectedPost.Tags.Add(tag);
                        if (!modifiedPosts.Contains(selectedPost))
                            modifiedPosts.Add(selectedPost);
                    }
                }
                else if (e.NewValue == CheckState.Unchecked)
                {
                    if (selectedPost.Tags.Contains(tag))
                        selectedPost.Tags.Remove(tag);
                    if (!modifiedPosts.Contains(selectedPost))
                        modifiedPosts.Add(selectedPost);
                }

                setModifiedStyle(selectedItem);
            }
            if (filterMode == FilterMode.NonModificati)
                refreshListView();
        }
        #endregion

        #region Gestione menu e toolbar
        void actSave_Update(object sender, System.EventArgs e)
        {
            actSave.Enabled = this.modifiedPosts != null && this.modifiedPosts.Count > 0;
        }

        private void save(object sender, EventArgs e)
        {
            try
            {
                IPostService service = ServiceRepository.Factory.GetPostService();

                foreach (var post in modifiedPosts)
                {
                    service.ChangeCategories(post);
                    service.ChangeTags(post);
                }

                MsgBox.Info(Resources.SaveCorrect);

                // aggiorno il feedback visivo sui post modificati
                foreach (ListViewItem postItem in lstPosts.Items)
                {
                    Post post = postItem.Tag as Post;
                    if (modifiedPosts.Contains(post))
                        setUnmodifiedStyle(postItem);
                }

                modifiedPosts.Clear();
            }
            catch (ConnectionException)
            {
                MsgBox.Error(Resources.ConnectionError);
            }
        }

        private void exit(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (modifiedPosts.Count > 0 &&
                !MsgBox.Confirm(Resources.ApplicationClosing))
            {
                e.Cancel = true;
            }
        }

        private void actFetch_Update(object sender, System.EventArgs e)
        {
            int n;
            actFetch.Enabled = int.TryParse(cmbNumberOfPosts.Text, out n) && n > 0;
        }

        private void actFetch_Execute(object sender, System.EventArgs e)
        {
            if (modifiedPosts.Count == 0 ||
                MsgBox.Confirm(Resources.ConfirmFetch))
            {
                int n;
                if (int.TryParse(cmbNumberOfPosts.Text, out n))
                    fillPosts(n);
            }
        }

        private void actOptions_Execute(object sender, System.EventArgs e)
        {
            OptionsForm f = new OptionsForm();
            if (f.ShowDialog() == DialogResult.OK && 
                (modifiedPosts.Count == 0 ||
                MsgBox.Confirm(Resources.ConfirmFetch)))
            { 
                // empty all data and reload categories
                this.lstPosts.Items.Clear();
                this.categories.Clear();
                this.modifiedPosts.Clear();
                this.posts.Clear();
                this.fillCategories();
            }
        }

        private void actRefreshCategories_Execute(object sender, System.EventArgs e)
        {
            if (modifiedPosts.Count == 0 ||
                MsgBox.Confirm(Resources.ConfirmFetch))
            {
                this.categories.Clear();
                this.modifiedPosts.Clear();
                fillCategories();
                lstPosts_SelectedIndexChanged(null, EventArgs.Empty);
            }
        }

        private void actInfo_Execute(object sender, System.EventArgs e)
        {
            new AboutBox1().ShowDialog();
        }
        #endregion

        #region Gestione Tags
        private List<string> tags = new List<string>();

        private void actNewTag_Execute(object sender, EventArgs e)
        {
            string newTag = this.txtNewTag.Text.Trim();
            if (!this.tags.Contains(newTag))
            {
                txtNewTag.Text = string.Empty;
                tags.Add(newTag);
                refreshTagsList();
            }
        }

        private bool refreshingTags = false;
        private void refreshTagsList()
        {
            try
            {
                refreshingTags = true;

                chkTags.DataSource = null;
                chkTags.DataSource = tags;

                string previousSelection = (string)cmbTags.SelectedItem;
                cmbTags.DataSource = null;
                List<string> cmbTagsSource = new List<string>(tags);
                cmbTagsSource.Insert(0, string.Empty);
                cmbTags.DataSource = cmbTagsSource;
                if (cmbTagsSource.Contains(previousSelection))
                    cmbTags.SelectedItem = previousSelection;
            }
            finally
            {
                refreshingTags = false;
                cmbTags_SelectedIndexChanged(cmbTags, EventArgs.Empty);
            }
        }

        private void refreshTagsList(IList<Post> posts)
        {
            tags.Clear();

            foreach (var post in posts)
            {
                foreach (var tag in post.Tags)
                {
                    if (!tags.Contains(tag))
                        tags.Add(tag);
                }
            }

            refreshTagsList();
        }

        private void actNewTag_Update(object sender, EventArgs e)
        {
            actNewTag.Enabled = txtNewTag.Text.Trim().Length > 0;
        }

        private void txtNewTag_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return)
            {
                e.Handled = true;
                actNewTag.DoExecute();
            }
        }
        #endregion

        #region Backup & restore
        private void actRestore_Execute(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                IPostService service = ServiceRepository.Factory.GetPostService();
                IList<Post> postsToRestore = service.Restore(openFileDialog1.FileName);

                if (postsToRestore != null && postsToRestore.Count > 0 &&
                    MsgBox.Confirm(
                        string.Format(Resources.ConfirmRestore, postsToRestore.Count)))
                {
                    service.BatchUpdate(postsToRestore);
                    MsgBox.Info(Resources.RestoreCorrect);
                }
            }
        }

        private void actBackup_Update(object sender, EventArgs e)
        {
        }

        private void actBackup_Execute(object sender, EventArgs e)
        {
            using (BackupForm form = new BackupForm())
            {
                form.ShowDialog();
            }
        }
        #endregion
    }
}
